package de.tabiteuea.gui.handlers;

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;

import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamConstants;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
import de.tabiteuea.gui.utils.ITabiXMLConstants;

import org.eclipse.core.commands.AbstractHandler;
import org.eclipse.core.commands.ExecutionEvent;
import org.eclipse.core.commands.ExecutionException;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.PlatformUI;

import de.tabiteuea.gui.UIView;
import de.tabiteuea.gui.dialogs.OpenFileDialog;
import de.tabiteuea.gui.models.InputModel;
import de.tabiteuea.gui.models.Language;
import de.tabiteuea.gui.models.ModelProvider;
import de.tabiteuea.gui.wizards.Messages;

/**
 * Handler opens a *.tabi project file, parses it and sets the according model
 * 
 * @author Sebastian Lippert
 * 
 */

public class OpenProjectHandler extends AbstractHandler {

	/**
	 * Execute handler
	 */

	@Override
	public Object execute(ExecutionEvent event) throws ExecutionException {

		// get ModelProvider
		ModelProvider provider = ModelProvider.getInstance();
		String savePath = "";

		IWorkbenchWindow activeWorkbenchWindow = PlatformUI.getWorkbench()
				.getActiveWorkbenchWindow();
		IWorkbenchPage activePage = activeWorkbenchWindow.getActivePage();

		// call file dialog to locate the project file
		try {
			OpenFileDialog fileDialog = new OpenFileDialog(
					activeWorkbenchWindow.getShell(),
					Messages.OpenFileDialog_open_project,
					new String[] { "*.tabi" });

			// fileDialog.isInterrupted() == cancel button was pushed
			if (!fileDialog.isInterrupted()) {

				// get absolute path to file from dialog
				savePath = fileDialog.getPath();

				// parse selected project file
				InputModel model = parseProjectFile(savePath);
				StringBuffer corpus = new StringBuffer();

				// special XML parsing
				if (model.getFilePath().endsWith(".xml")) {
					corpus = parseXML(model.getFilePath());
				} else {

					// parse corpus
					BufferedReader myBuffer = new BufferedReader(
							new FileReader(model.getFilePath()));
					String line = null;
					/*
					 * reading the input line by line
					 * 
					 * NOTE: After the last line a newline will be appended. Its
					 * cheaper to handle this later than to remove the last
					 * newLine
					 */
					while ((line = myBuffer.readLine()) != null) {

						corpus.append(line);
						corpus.append(System.getProperty("line.separator"));

					}

					// close buffer and set corpus
					myBuffer.close();
				}
				model.setCorpus(corpus.toString());

				// set global model
				provider.setInputModel(model);

				// try to find UI-View and activate or show it
				try {
					UIView view = (UIView) activePage.findView(UIView.ID);
					if (view == null)
						view = (UIView) activePage.showView(UIView.ID);
					else
						activePage.activate(view);

					// load model
					view.loadModel(provider.getInputModel());

				} catch (PartInitException e) {
					throw new RuntimeException(e);
				}

			}
		} catch (IOException e1) {
			MessageDialog.openError(
					activeWorkbenchWindow.getShell(),
					Messages.NewProjectWizardPage_error,
					Messages.NewProjectWizardPage_couldNotOpenFile + " "
							+ e1.getLocalizedMessage());
			execute(event);
		} catch (XMLStreamException e2) {
			MessageDialog.openError(
					activeWorkbenchWindow.getShell(),
					Messages.NewProjectWizardPage_error,
					Messages.NewProjectWizardPage_couldNotOpenFile + " "
							+ e2.getLocalizedMessage());
			execute(event);
			e2.printStackTrace();
		}

		return null;
	}

	/*
	 * Method is specialized to parse XML files. By convention only the
	 * characters within <text> tags should be read. So we search this tag an
	 * append the following text to our buffer.
	 */

	private StringBuffer parseXML(String path) throws FileNotFoundException,
			XMLStreamException {

		StringBuffer corpus = new StringBuffer();
		String encoding = null;

		// try to read encoding of the xml file
		XMLInputFactory factory = XMLInputFactory.newInstance();
		XMLStreamReader reader = factory
				.createXMLStreamReader(new FileInputStream(path));
		encoding = reader.getCharacterEncodingScheme();

		// use encoding if it was detected, otherwise the system default
		// encoding will be used
		if (encoding != null)
			reader = factory.createXMLStreamReader(new FileInputStream(path),
					encoding);

		// read file
		while (reader.hasNext()) {
			int event = reader.next();

			switch (event) {
			case XMLStreamConstants.END_DOCUMENT:
				reader.close();
				break;
			case XMLStreamConstants.START_ELEMENT:

				// find the <text> element
				if (reader.getLocalName().equals(ITabiXMLConstants.TEXT_TAG)) {

					// next element should be out input
					reader.next();
					if (!reader.isWhiteSpace())
						corpus.append(reader.getText());

				}
				break;

			}
		}

		return corpus;

	}

	/*
	 * Parse project XML file, get values and attributes and create new model
	 * which will be returned
	 */

	private InputModel parseProjectFile(String savePath) {

		InputModel model = new InputModel();
		model.setSavePath(savePath);

		try {

			// get reader
			XMLInputFactory factory = XMLInputFactory.newInstance();
			XMLStreamReader reader = factory
					.createXMLStreamReader((new FileInputStream(savePath)));

			// read file
			while (reader.hasNext()) {
				int event = reader.next();

				switch (event) {
				case XMLStreamConstants.END_DOCUMENT:
					reader.close();
					break;
				case XMLStreamConstants.START_ELEMENT:

					// check if the XML file is an *.tabi file
					// (done by the check of the project tag)
					if (reader.getLocalName().equals(
							ITabiXMLConstants.PROJECT_TAG)) {

						String attrName;
						String attrValue;
						for (int i = 0; i < reader.getAttributeCount(); i++) {

							attrName = reader.getAttributeLocalName(i);
							attrValue = reader.getAttributeValue(i);

							// read attributes and set the according attribute
							// at the model

							if (attrName
									.equals(ITabiXMLConstants.PROJECT_NAME_ATTR))
								model.setProjectName(attrValue);
							else if (attrName
									.equals(ITabiXMLConstants.FILE_PATH_ATTR))
								model.setFilePath(attrValue);
							else if (attrName
									.equals(ITabiXMLConstants.FILE_NAME_ATTR))
								model.setFileName(attrValue);
							else if (attrName
									.equals(ITabiXMLConstants.TEXT_LANGUAGE_ATTR))
								model.setTextLanguage(detectLanguage(attrValue));
							else if (attrName
									.equals(ITabiXMLConstants.BASEFORM_ATTR)) {
								if (attrValue.equals("true"))
									model.setUseBaseform(true);
								else
									model.setUseBaseform(false);
							} else if (attrName
									.equals(ITabiXMLConstants.WORD_DELIMIT_ATTR))
								model.setWordDelimiter(attrValue);
							else if (attrName
									.equals(ITabiXMLConstants.SENTENCE_DELIMIT_ATTR))
								model.setSentenceDelimiter(attrValue);
							else if (attrName
									.equals(ITabiXMLConstants.WORD_DELIMITER_IS_REGEX_ATTR)) {
								if (attrValue.equals("true"))
									model.setWordDelIsRegExp(true);
								else
									model.setWordDelIsRegExp(false);
							} else if (attrName
									.equals(ITabiXMLConstants.SENTENCE_DELIMITER_IS_REGEX_ATTR)) {
								if (attrValue.equals("true"))
									model.setSenDelIsRegExp(true);
								else
									model.setSenDelIsRegExp(false);
							}
						}

					}

					break;
				case XMLStreamConstants.CHARACTERS:
					if (!reader.isWhiteSpace())
						model.addWordToWordList(reader.getText());
					break;
				case XMLStreamConstants.END_ELEMENT:
					break;
				default:
					break;
				}
			}

		} catch (FileNotFoundException e) {
			e.printStackTrace();
			// return empty model if something went wrong
			return new InputModel();
		} catch (XMLStreamException e) {
			e.printStackTrace();
			// return empty model if something went wrong
			return new InputModel();
		}

		return model;

	}

	// convert language String into an Enum
	private Language detectLanguage(String attrValue) {

		if (attrValue.equals("en"))
			return Language.en;
		if (attrValue.equals("de"))
			return Language.de;

		return Language.en;
	}
}
